/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License");  you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * http//www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 *
 * The Original Code is ART OWL API.
 *
 * The Initial Developer of the Original Code is University of Roma Tor Vergata.
 * Portions created by University of Roma Tor Vergata are Copyright (C) 2009.
 * All Rights Reserved.
 *
 * The ART OWL API were developed by the Artificial Intelligence Research Group
 * (art.uniroma2.it) at the University of Roma Tor Vergata
 * Current information about the ART OWL API can be obtained at 
 * http://art.uniroma2.it/owlart
 *
 */

package it.uniroma2.art.owlart.models.impl;

import java.util.Set;

import it.uniroma2.art.owlart.exceptions.ModelAccessException;
import it.uniroma2.art.owlart.exceptions.ModelUpdateException;
import it.uniroma2.art.owlart.model.ARTBNode;
import it.uniroma2.art.owlart.model.ARTLiteral;
import it.uniroma2.art.owlart.model.ARTResource;
import it.uniroma2.art.owlart.model.ARTURIResource;
import it.uniroma2.art.owlart.model.NodeFilters;
import it.uniroma2.art.owlart.models.BaseRDFTripleModel;
import it.uniroma2.art.owlart.models.SKOSXLModel;
import it.uniroma2.art.owlart.models.SKOSXLReasoner;
import it.uniroma2.art.owlart.navigation.ARTLiteralIterator;
import it.uniroma2.art.owlart.navigation.ARTResourceIterator;
import it.uniroma2.art.owlart.navigation.RDFIteratorImpl;
import it.uniroma2.art.owlart.utilities.RDFIterators;
import it.uniroma2.art.owlart.vocabulary.RDF;
import it.uniroma2.art.owlart.vocabulary.SKOS;
import it.uniroma2.art.owlart.vocabulary.SKOSXL;

/**
 * @author Armando Stellato &lt;stellato@info.uniroma2.it&gt;
 * 
 */
public class SKOSXLModelImpl extends SKOSModelImpl implements SKOSXLModel, SKOSXLReasoner {

	private boolean xLabelReasoning;

	public SKOSXLModelImpl(BaseRDFTripleModel baseRep) {
		super(baseRep);

		// REASONING CONFIGURATION
		if (baseRep instanceof SKOSXLReasoner) {
			xLabelReasoning = ((SKOSXLReasoner) baseRep).supportsExtendedLabels();
		} else {
			xLabelReasoning = false;
		}
	}
	
	public boolean isXLabel(ARTResource resource, ARTResource... graphs) throws ModelAccessException {
		return baseRep.hasTriple(resource, RDF.Res.TYPE, SKOSXL.Res.LABEL, true, graphs);
	}

	public ARTURIResource addXLabel(String xLabelURI, ARTLiteral literalForm, ARTResource... graphs)
			throws ModelUpdateException {
		ARTURIResource xLabel = baseRep.createURIResource(xLabelURI);
		baseRep.addTriple(xLabel, SKOSXL.Res.LITERALFORM, literalForm, graphs);
		baseRep.addTriple(xLabel, RDF.Res.TYPE, SKOSXL.Res.LABEL, graphs);
		return xLabel;
	}

	public ARTURIResource addXLabel(String xLabelURI, String literalForm, String language,
			ARTResource... graphs) throws ModelUpdateException {
		ARTLiteral xLabelLiteral = baseRep.createLiteral(literalForm, language);
		return addXLabel(xLabelURI, xLabelLiteral, graphs);
	}

	public ARTBNode addXLabel(ARTLiteral literalForm, ARTResource... graphs) throws ModelUpdateException {
		ARTBNode xLabel = baseRep.createBNode();
		baseRep.addTriple(xLabel, SKOSXL.Res.LITERALFORM, literalForm, graphs);
		baseRep.addTriple(xLabel, RDF.Res.TYPE, SKOSXL.Res.LABEL, graphs);
		return xLabel;
	}

	public ARTBNode addXLabel(String literalForm, String language, ARTResource... graphs)
			throws ModelUpdateException {
		ARTLiteral xLabelLiteral = baseRep.createLiteral(literalForm, language);
		return addXLabel(xLabelLiteral, graphs);
	}

	public void addAltXLabel(ARTURIResource skosConcept, ARTResource xlabel, ARTResource... graphs)
			throws ModelAccessException, ModelUpdateException {
		baseRep.addTriple(skosConcept, SKOSXL.Res.ALTLABEL, xlabel, graphs);

	}

	public void addAltXLabel(ARTURIResource skosConcept, ARTLiteral literalForm, ARTResource... graphs)
			throws ModelAccessException, ModelUpdateException {
		ARTBNode bNodeXLabel = addXLabel(literalForm, graphs);
		baseRep.addTriple(skosConcept, SKOSXL.Res.ALTLABEL, bNodeXLabel, graphs);
	}

	public void addAltXLabel(ARTURIResource skosConcept, String literalForm, String language,
			ARTResource... graphs) throws ModelAccessException, ModelUpdateException {
		ARTBNode bNodeXLabel = addXLabel(literalForm, language, graphs);
		baseRep.addTriple(skosConcept, SKOSXL.Res.ALTLABEL, bNodeXLabel, graphs);

	}

	public void addHiddenXLabel(ARTURIResource skosConcept, ARTResource xlabel, ARTResource... graphs)
			throws ModelAccessException, ModelUpdateException {
		baseRep.addTriple(skosConcept, SKOSXL.Res.HIDDENLABEL, xlabel, graphs);
	}

	public void addHiddenXLabel(ARTURIResource skosConcept, ARTLiteral literalForm, ARTResource... graphs)
			throws ModelAccessException, ModelUpdateException {
		ARTBNode bNodeXLabel = addXLabel(literalForm, graphs);
		baseRep.addTriple(skosConcept, SKOSXL.Res.HIDDENLABEL, bNodeXLabel, graphs);

	}

	public void addHiddenXLabel(ARTURIResource skosConcept, String literalForm, String language,
			ARTResource... graphs) throws ModelAccessException, ModelUpdateException {
		ARTBNode bNodeXLabel = addXLabel(literalForm, language, graphs);
		baseRep.addTriple(skosConcept, SKOSXL.Res.HIDDENLABEL, bNodeXLabel, graphs);

	}

	public void changeLiteralForm(ARTResource xLabel, ARTLiteral literalForm, ARTResource... graphs)
			throws ModelUpdateException {
		baseRep.deleteTriple(xLabel, SKOSXL.Res.LITERALFORM, NodeFilters.ANY, graphs);
		baseRep.addTriple(xLabel, SKOSXL.Res.LITERALFORM, literalForm, graphs);
	}

	public void changeLiteralForm(ARTResource xLabel, String literalForm, String language,
			ARTResource... graphs) throws ModelUpdateException {
		baseRep.deleteTriple(xLabel, SKOSXL.Res.LITERALFORM, NodeFilters.ANY, graphs);
		ARTLiteral xLabelLiteral = baseRep.createLiteral(literalForm, language);
		baseRep.addTriple(xLabel, SKOSXL.Res.LITERALFORM, xLabelLiteral, graphs);
	}

	public void deleteXLabel(ARTResource xLabel, ARTResource... graphs) throws ModelUpdateException {
		baseRep.deleteTriple(xLabel, NodeFilters.ANY, NodeFilters.ANY, graphs);
		baseRep.deleteTriple(NodeFilters.ANY, NodeFilters.ANY, xLabel, graphs);
	}

	public void detachXLabel(ARTResource skosConcept, ARTResource xLabel, ARTResource... graphs)
			throws ModelUpdateException {
		baseRep.deleteTriple(skosConcept, NodeFilters.ANY, xLabel, graphs);
	}

	public ARTLiteral getLiteralForm(ARTResource xLabel, ARTResource... graphs) throws ModelAccessException {
		ARTLiteralIterator litIt = listValuesOfSubjDTypePropertyPair(xLabel, SKOSXL.Res.LITERALFORM, false,
				graphs);
		ARTLiteral result;
		if (litIt.streamOpen()) {
			result = litIt.getNext();
		} else
			result = null;
		litIt.close();
		return result;
	}

	protected class LanguageFilteredXLabelIterator extends RDFIteratorImpl<ARTResource> implements
			ARTResourceIterator {

		String language;
		ARTResourceIterator xLabIt;
		ARTResource fetchedXLabel;
		ARTResource[] graphs;

		LanguageFilteredXLabelIterator(ARTResourceIterator xLabIt, String language, ARTResource[] graphs) {
			this.xLabIt = xLabIt;
			this.graphs = graphs;
			this.language = language;
		}

		public void close() throws ModelAccessException {
			xLabIt.close();
		}

		public boolean streamOpen() throws ModelAccessException {
			ARTResource temp;
			while (xLabIt.streamOpen() == true && fetchedXLabel == null) {
				temp = xLabIt.next();
				ARTLiteral lit = getLiteralForm(temp, graphs);
				if (lit.getLanguage().equals(language))
					fetchedXLabel = temp;
			}
			if (fetchedXLabel != null)
				return true;
			else
				return false;
		}

		public ARTResource getNext() throws ModelAccessException {
			ARTResource temp = fetchedXLabel;
			fetchedXLabel = null;
			return temp;
		}

	}

	public ARTResource getPrefXLabel(ARTURIResource skosConcept, String languageTag, ARTResource... graphs)
			throws ModelAccessException {
		ARTResourceIterator labIt = listValuesOfSubjObjPropertyPair(skosConcept, SKOSXL.Res.PREFLABEL, false,
				graphs);
		// not possible to use DesiredLanguageLabelPredicate because we have no direct iterator over labels
		ARTResourceIterator labLangIt = new LanguageFilteredXLabelIterator(labIt, languageTag, graphs);
		ARTResource result;
		if (labLangIt.streamOpen()) {
			result = labLangIt.getNext();
		} else
			result = null;
		labLangIt.close();
		return result;
	}

	public ARTResourceIterator getRelatedLabels(ARTResource xLabel, ARTResource... graphs)
			throws ModelAccessException {
		ARTResourceIterator it = listValuesOfSubjObjPropertyPair(xLabel, SKOSXL.Res.LABELRELATION, true,
				graphs);
		return it;
	}

	public ARTResourceIterator listAltXLabels(ARTURIResource skosConcept, ARTResource... graphs)
			throws ModelAccessException {
		return listValuesOfSubjObjPropertyPair(skosConcept, SKOSXL.Res.ALTLABEL, false, graphs);
	}

	public ARTResourceIterator listAltXLabels(ARTURIResource skosConcept, String languageTag,
			ARTResource... graphs) throws ModelAccessException {
		ARTResourceIterator labIt = listValuesOfSubjObjPropertyPair(skosConcept, SKOSXL.Res.ALTLABEL, false,
				graphs);
		// not possible to use DesiredLanguageLabelPredicate because we have no direct iterator over labels
		return new LanguageFilteredXLabelIterator(labIt, languageTag, graphs);
	}

	public ARTResourceIterator listHiddenXLabels(ARTURIResource skosConcept, ARTResource... graphs)
			throws ModelAccessException {
		return listValuesOfSubjObjPropertyPair(skosConcept, SKOSXL.Res.HIDDENLABEL, false, graphs);
	}

	public ARTResourceIterator listHiddenXLabels(ARTURIResource skosConcept, String languageTag,
			ARTResource... graphs) throws ModelAccessException {
		ARTResourceIterator labIt = listValuesOfSubjObjPropertyPair(skosConcept, SKOSXL.Res.HIDDENLABEL,
				false, graphs);
		// not possible to use DesiredLanguageLabelPredicate because we have no direct iterator over labels
		return new LanguageFilteredXLabelIterator(labIt, languageTag, graphs);
	}

	public ARTResourceIterator listPrefXLabels(ARTURIResource skosConcept, ARTResource... graphs)
			throws ModelAccessException {
		return listValuesOfSubjObjPropertyPair(skosConcept, SKOSXL.Res.PREFLABEL, false, graphs);
	}

	public void setPrefXLabel(ARTURIResource skosConcept, ARTResource xlabel, boolean delete,
			ARTResource... graphs) throws ModelAccessException, ModelUpdateException {
		// previous preferred xlabel removal (label to be replaced is the one with the same language of the
		// new one)
		ARTResource prevLabel = getPrefXLabel(skosConcept, getLiteralForm(xlabel, graphs).getLanguage(),
				graphs);
		if (prevLabel != null) {
			if (delete) {
				deleteXLabel(prevLabel, graphs);
			} else {
				baseRep.deleteTriple(skosConcept, SKOSXL.Res.PREFLABEL, prevLabel, graphs);
			}
		}
		// adding given xlabel as preferred label
		baseRep.addTriple(skosConcept, SKOSXL.Res.PREFLABEL, xlabel, graphs);
	}

	public void setPrefXLabel(ARTURIResource skosConcept, ARTResource xlabel, ARTResource... graphs)
			throws ModelAccessException, ModelUpdateException {
		setPrefXLabel(skosConcept, xlabel, true, graphs);
	}

	public void setPrefXLabel(ARTURIResource skosConcept, ARTLiteral literalform, boolean delete,
			ARTResource... graphs) throws ModelAccessException, ModelUpdateException {
		// previous preferred xlabel removal (label to be replaced is the one with the same language of the
		// new one)
		ARTResource prevLabel = getPrefXLabel(skosConcept, literalform.getLanguage(), graphs);
		if (prevLabel != null) {
			if (delete) {
				deleteXLabel(prevLabel, graphs);
			} else {
				baseRep.deleteTriple(skosConcept, SKOSXL.Res.PREFLABEL, prevLabel, graphs);
			}
		}
		// adding new xlabel and setting it as preferred label
		ARTBNode newXLabel = addXLabel(literalform, graphs);
		baseRep.addTriple(skosConcept, SKOSXL.Res.PREFLABEL, newXLabel, graphs);
	}

	public void setPrefXLabel(ARTURIResource skosConcept, ARTLiteral literalform, ARTResource... graphs)
			throws ModelAccessException, ModelUpdateException {
		setPrefXLabel(skosConcept, literalform, true, graphs);
	}

	public void setPrefXLabel(ARTURIResource skosConcept, String literalForm, String language,
			boolean delete, ARTResource... graphs) throws ModelAccessException, ModelUpdateException {
		ARTLiteral xLabelLiteral = baseRep.createLiteral(literalForm, language);
		setPrefXLabel(skosConcept, xLabelLiteral, delete, graphs);
	}

	public void setPrefXLabel(ARTURIResource skosConcept, String literalform, String language,
			ARTResource... graphs) throws ModelAccessException, ModelUpdateException {
		setPrefXLabel(skosConcept, literalform, language, true, graphs);
	}

	/********************************
	 *** SKOSXL REASONING METHODS ***
	 ********************************/

	public boolean supportsExtendedLabels() {
		return xLabelReasoning;
	}

	/********************************************************************
	 *** METHODS REDEFINED FROM SKOS MODEL TO SUPPORT XLABEL REASONING **
	 ********************************************************************/

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * it.uniroma2.art.owlart.models.impl.SKOSModelImpl#listAltLabels(it.uniroma2.art.owlart.model.ARTURIResource
	 * , boolean, it.uniroma2.art.owlart.model.ARTResource[])
	 * 
	 * if no reasoning is availablle supporting xLabel reasoning, then invokes the trivial reasoning to
	 * transform literalForms of xLabels into skos labels
	 */
	public ARTLiteralIterator listAltLabels(ARTURIResource skosConcept, boolean inferred,
			ARTResource... graphs) throws ModelAccessException {
		return resolveXLabelsPropertyChain(skosConcept, SKOSXL.Res.ALTLABEL, SKOS.Res.ALTLABEL, inferred,
				graphs);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seeit.uniroma2.art.owlart.models.impl.SKOSModelImpl#listHiddenLabels(it.uniroma2.art.owlart.model.
	 * ARTURIResource, boolean, it.uniroma2.art.owlart.model.ARTResource[])
	 * 
	 * if no reasoning is availablle supporting xLabel reasoning, then invokes the trivial reasoning to
	 * transform literalForms of xLabels into skos labels
	 */
	public ARTLiteralIterator listHiddenLabels(ARTURIResource skosConcept, boolean inferred,
			ARTResource... graphs) throws ModelAccessException {
		return resolveXLabelsPropertyChain(skosConcept, SKOSXL.Res.HIDDENLABEL, SKOS.Res.HIDDENLABEL,
				inferred, graphs);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seeit.uniroma2.art.owlart.models.impl.SKOSModelImpl#listPreferredLabels(it.uniroma2.art.owlart.model.
	 * ARTURIResource, boolean, it.uniroma2.art.owlart.model.ARTResource[])
	 * 
	 * if no reasoning is availablle supporting xLabel reasoning, then invokes the trivial reasoning to
	 * transform literalForms of xLabels into skos labels
	 */
	public ARTLiteralIterator listPrefLabels(ARTURIResource skosConcept, boolean inferred,
			ARTResource... graphs) throws ModelAccessException {
		return resolveXLabelsPropertyChain(skosConcept, SKOSXL.Res.PREFLABEL, SKOS.Res.PREFLABEL, inferred,
				graphs);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * it.uniroma2.art.owlart.models.impl.SKOSModelImpl#listAltLabels(it.uniroma2.art.owlart.model.ARTURIResource
	 * , java.lang.String, boolean, it.uniroma2.art.owlart.model.ARTResource[])
	 */
	public ARTLiteralIterator listAltLabels(ARTURIResource skosConcept, String languageTag, boolean inferred,
			ARTResource... graphs) throws ModelAccessException {
		return resolveXLabelsPropertyChain(skosConcept, SKOSXL.Res.ALTLABEL, SKOS.Res.ALTLABEL, languageTag,
				inferred, graphs);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seeit.uniroma2.art.owlart.models.impl.SKOSModelImpl#listHiddenLabels(it.uniroma2.art.owlart.model.
	 * ARTURIResource, java.lang.String, boolean, it.uniroma2.art.owlart.model.ARTResource[])
	 */
	public ARTLiteralIterator listHiddenLabels(ARTURIResource skosConcept, String languageTag,
			boolean inferred, ARTResource... graphs) throws ModelAccessException {
		return resolveXLabelsPropertyChain(skosConcept, SKOSXL.Res.HIDDENLABEL, SKOS.Res.HIDDENLABEL,
				languageTag, inferred, graphs);
	}

	/* (non-Javadoc)
	 * @see it.uniroma2.art.owlart.models.impl.SKOSModelImpl#getPrefLabel(it.uniroma2.art.owlart.model.ARTURIResource, java.lang.String, boolean, it.uniroma2.art.owlart.model.ARTResource[])
	 */
	public ARTLiteral getPrefLabel(ARTURIResource skosConcept, String languageTag, boolean inferred,
			ARTResource... graphs) throws ModelAccessException {
		ARTLiteralIterator it = resolveXLabelsPropertyChain(skosConcept, SKOSXL.Res.PREFLABEL, SKOS.Res.PREFLABEL,
				languageTag, inferred, graphs);
		while (it.hasNext()) {
			ARTLiteral literal = it.next();
			if (languageTag.equals(literal.getLanguage()))
				return literal;
		}
		return null;
	}

	/*********************************
	 *** TRIVIAL REASONING METHODS ***
	 *********************************/

	protected ARTLiteralIterator resolveXLabelsPropertyChain(ARTURIResource concept,
			ARTURIResource xLabelProperty, ARTURIResource labelProperty, boolean inferred,
			ARTResource... graphs) throws ModelAccessException {

		ARTLiteralIterator lit = RDFIterators.toLiteralIterator(getOWLModel().listValuesOfSubjPredPair(
				concept, labelProperty, inferred, graphs));

		if (!inferred || supportsExtendedLabels())
			return lit;

		Set<ARTLiteral> set = RDFIterators.getSetFromIterator(lit);
		ARTResourceIterator resIt = listValuesOfSubjObjPropertyPair(concept, xLabelProperty, inferred, graphs);
		while (resIt.streamOpen()) {
			ARTResource xLabel = resIt.getNext();
			set.add(getLiteralForm(xLabel, graphs));
		}
		resIt.close();
		return new ARTLiteralIteratorImpl(set.iterator());

	}

	protected ARTLiteralIterator resolveXLabelsPropertyChain(ARTURIResource concept,
			ARTURIResource xLabelProperty, ARTURIResource labelProperty, String languageTag,
			boolean inferred, ARTResource... graphs) throws ModelAccessException {

		if (languageTag == null)
			throw new IllegalArgumentException("Argument 'languageTag' cannot be null!");
		
		ARTLiteralIterator lit = new LiteralIteratorFilteringLanguage(getOWLModel().listValuesOfSubjPredPair(
				concept, labelProperty, inferred, graphs), languageTag);

		if (!inferred || supportsExtendedLabels())
			return lit;

		Set<ARTLiteral> set = RDFIterators.getSetFromIterator(lit);
		ARTResourceIterator xlabIt = listValuesOfSubjObjPropertyPair(concept, xLabelProperty, inferred,
				graphs);
		ARTResourceIterator filtLabIt = new LanguageFilteredXLabelIterator(xlabIt, languageTag, graphs);
		while (filtLabIt.streamOpen()) {
			ARTResource xLabel = filtLabIt.getNext();
			set.add(getLiteralForm(xLabel, graphs));
		}
		filtLabIt.close();
		return new ARTLiteralIteratorImpl(set.iterator());
	}

}
